home *** CD-ROM | disk | FTP | other *** search
Wrap
/* * $Id: vertexarray.c,v 1.3 2001/02/05 16:56:03 tfrieden Exp $ * * $Date: 2001/02/05 16:56:03 $ * $Revision: 1.3 $ * * (C) 1999 by Hyperion * All rights reserved * * This file is part of the MiniGL library project * See the file Licence.txt for more details * */ /* ** glDrawArrays pipeline by Christian 'SuRgEoN' Michael ** Thanks to Olivier Fabre for bug-hunting */ /* ** revised 07-04-02: ** ** In some cases A_DrawTriangles didn't use the correct ** start position during backface-culling ** ** Support for GL_EXT_compiled_vertex_array was added. ** ** When Clip_Volume_Bypass is GL_FALSE, it will wrap ** to GLDrawElements (at the very beginning of GLDrawArrays, ** which is ofcourse not an optimal solution. ** Anyways, the use if glDrawArrays is probably rare in case ** of compiled arrays, but it should be supported. ** One possible solution to this is to make the default ** W3D_VertexPointer point to the W3D_Vertex struct inside ** the vertexbuffer like for elements, but it would probably ** lower the general performance of glDrawArrays. */ #include "sysinc.h" #include "vertexarray.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> /* NOTE: Transformation and clipping pipeline can be turned on/off with glDisable/glEnable(MGL_ARRAY_TRANSFORMATIONS) before the pointers are set. The MGL_FLATFAN will work regardless of pipeline state. Tricky stuff: Problem 1: We don't want to copy texcoords unless we really have to (clipping) and we don't want to write the w-coords to application-arrays since a texcoordpointer is allowed to have a stride of 2. Current solution: We allocate some memory exclusively for the w-coords in order to cope with any texcoordstride. The relative w-coord buffer offset is calculated whenever texcoordpointer is set. Problem 2: Clipping events are expensive because texcoords and colors currently need to be converted and copied to the vertexbuffer. Solution: ?? Currently, a sort of guardband is used in order to avoid clipping in most cases. TODO: - Implement remaining GL primitives. - Implement MGL_UBYTE_ARGB color-format (Voodoo3 native) - Write pointer-wrapper for glInterleavedArrays and proper optimized conversion-routines for clipping. - Implement multitexturing (virtual TMU for now). */ //NYI: void A_DrawPoints (GLcontext context, const int first, const int count); void A_DrawLines (GLcontext context, const int first, const int count); void A_DrawLineStrip (GLcontext context, const int first, const int count); void A_DrawLineLoop (GLcontext context, const int first, const int count); void A_DrawQuads (GLcontext context, const int first, const int count); void A_DrawQuadStrip (GLcontext context, const int first, const int count); //implemented: void A_DrawTriFan (GLcontext context, const int first, const int count); void A_DrawTriStrip (GLcontext context, const int first, const int count); void A_DrawTriangles (GLcontext context, const int first, const int count); void A_DrawPolygon (GLcontext context, const int first, const int count); void A_DrawFlatFan (GLcontext context, const int first, const int count); extern void TMA_Start(LockTimeHandle *handle); extern GLboolean TMA_Check(LockTimeHandle *handle); extern void fog_Set(GLcontext context); static INLINE void PreDraw(GLcontext context); static INLINE void PostDraw(GLcontext context); static GLboolean Reset_W3D_VertexPointer = GL_TRUE; int A_TransformArray (GLcontext context, const int first, const int size); void A_ToScreenArray(GLcontext context, const int first, const int size); extern void m_CombineMatrices(GLcontext context); //Culling: GLboolean DecideFrontface(GLcontext context, const MGLVertex *a, const MGLVertex *b, const MGLVertex *c, int sign); //Clipping (convert color and texcoords to vertexbuffer) void Convert_F_RGB(GLcontext context, int i); void Convert_F_RGBA(GLcontext context, int i); void Convert_UB_RGB(GLcontext context, int i); void Convert_UB_RGBA(GLcontext context, int i); void Convert_UB_BGR(GLcontext context, int i); void Convert_UB_BGRA(GLcontext context, int i); void Convert_UB_ARGB(GLcontext context, int i); //pointer to color/texcoord conversion routine for clipping events: Convfn Convert = (Convfn)Convert_F_RGBA; extern void AE_ClipPoly(GLcontext context, PolyBuffer *out, int clipstart, ULONG or_codes); //stuff for compiled arrays: added 07-04-2002 extern GLboolean Clip_Volume_Bypass; #define MAX_ELEMENTS 16384 //should be enough static UWORD e_wrap[MAX_ELEMENTS]; //should be enough //called from MGLInit: void Init_ArrayToElements_Warpper(void) { int i; for (i=0; i<MAX_ELEMENTS; i++) e_wrap[i] = i; } //called at the very beginning of GLDrawArrays in case clipping is needed with compiled arrays: extern void GLDrawElements(GLcontext context, GLenum mode, const GLsizei count, GLenum type, const GLvoid *indices); // end of compiled arrays support int A_TransformArray (GLcontext context, const int first, const int size) { int i; ULONG and_code, or_code, guard_band; #define OF_11 0 #define OF_12 4 #define OF_13 8 #define OF_14 12 #define OF_21 1 #define OF_22 5 #define OF_23 9 #define OF_24 13 #define OF_31 2 #define OF_32 6 #define OF_33 10 #define OF_34 14 #define OF_41 3 #define OF_42 7 #define OF_43 11 #define OF_44 15 #define a(x) (context->CombinedMatrix.v[OF_##x]) if(context->ArrayPointer.FixpointTrans == GL_TRUE) { static int m[16]; int tx,ty,tz,tw; int x,y,z; int *vp; ULONG local_outcode; UBYTE *vpointer; MGLVertex *v; int stride; #define CLIP_EPS ((int)((1e-7)*32768.f)) #define b(x) (m[OF_##x]) if(context->CombinedValid == GL_FALSE) { const float float2fix = 32768.f; m_CombineMatrices(context); b(11)=(int)(a(11)*float2fix); b(12)=(int)(a(12)*float2fix); b(13)=(int)(a(13)*float2fix); b(14)=(int)(a(14)*float2fix); b(21)=(int)(a(21)*float2fix); b(22)=(int)(a(22)*float2fix); b(23)=(int)(a(23)*float2fix); b(24)=(int)(a(24)*float2fix); b(31)=(int)(a(31)*float2fix); b(32)=(int)(a(32)*float2fix); b(33)=(int)(a(33)*float2fix); b(34)=(int)(a(34)*float2fix); b(41)=(int)(a(41)*float2fix); b(42)=(int)(a(42)*float2fix); b(43)=(int)(a(43)*float2fix); b(44)=(int)(a(44)*float2fix); } stride = context->ArrayPointer.vertexstride; vpointer = (context->ArrayPointer.verts + first*stride); and_code = 0xff; or_code = 0; v = &(context->VertexBuffer[first]); i = size; do { vp = (int *)vpointer; x = vp[0]; y = vp[1]; z = vp[2]; //pipelined transformations tx = x*b(11); ty = x*b(21); tz = x*b(31); tw = x*b(41); tx += y*b(12); ty += y*b(22); tz += y*b(32); tw += y*b(42); tx += z*b(13); ty += z*b(23); tz += z*b(33); tw += z*b(43); tx += b(14); ty += b(24); tz += b(34); tw += b(44); local_outcode = 0; if (tw < CLIP_EPS ) { local_outcode |= MGL_CLIP_NEGW; } if (-tw > tx) { local_outcode |= MGL_CLIP_LEFT; } else if (tx > tw) { local_outcode |= MGL_CLIP_RIGHT; } if (-tw > ty) { local_outcode |= MGL_CLIP_BOTTOM; } else if (ty > tw) { local_outcode |= MGL_CLIP_TOP; } if (-tw > tz) { local_outcode |= MGL_CLIP_BACK; } else if (tz > tw) { local_outcode |= MGL_CLIP_FRONT; } v->bx = (float)tx; v->by = (float)ty; v->bz = (float)tz; v->bw = (float)tw; v->outcode = local_outcode; and_code &= local_outcode; or_code |= local_outcode; v++; vpointer += stride; } while (--i); #undef b #undef CLIP_EPS } else { float a11,a12,a13,a14; float a21,a22,a23,a24; float a31,a32,a33,a34; float a41,a42,a43,a44; float *vp; float cw; float x,y,z; ULONG local_outcode; UBYTE *vpointer; MGLVertex *v; int stride; #define CLIP_EPS (1e-7) if(context->CombinedValid == GL_FALSE) m_CombineMatrices(context); a11=a(11); a12=a(12); a13=a(13); a14=a(14); a21=a(21); a22=a(22); a23=a(23); a24=a(24); a31=a(31); a32=a(32); a33=a(33); a34=a(34); a41=a(41); a42=a(42); a43=a(43); a44=a(44); stride = context->ArrayPointer.vertexstride; vpointer = (context->ArrayPointer.verts + first*stride); v = &(context->VertexBuffer[first]); i = size; do { vp = (float *)vpointer; x = vp[0]; y = vp[1]; z = vp[2]; v->bx = a11*x + a12*y + a13*z + a14; v->by = a21*x + a22*y + a23*z + a24; v->bz = a31*x + a32*y + a33*z + a34; v->bw = a41*x + a42*y + a43*z + a44; v++; vpointer += stride; } while (--i); //prepare culling; and_code = 0xff; or_code = 0; v = &context->VertexBuffer[first]; i = size; do { cw = v->bw; local_outcode = 0; if (cw < CLIP_EPS ) { local_outcode |= MGL_CLIP_NEGW; } if (-cw > v->bx) { local_outcode |= MGL_CLIP_LEFT; } else if (v->bx > cw) { local_outcode |= MGL_CLIP_RIGHT; } if (-cw > v->by) { local_outcode |= MGL_CLIP_BOTTOM; } else if (v->by > cw) { local_outcode |= MGL_CLIP_TOP; } if (-cw > v->bz) { local_outcode |= MGL_CLIP_BACK; } else if (v->bz > cw) { local_outcode |= MGL_CLIP_FRONT; } and_code &= local_outcode; or_code |= local_outcode; v->outcode = local_outcode; v++; } while (--i); #undef CLIP_EPS } #undef a if (and_code) return -1; if(or_code == 0 || (or_code & context->ClipFlags)) { return (int)or_code; } else { float gcw; ULONG guard_code; MGLVertex *v; i = size; guard_band = 0; v = &context->VertexBuffer[first]; do { if(v->outcode) { gcw = v->bw * 2.0; guard_code = 0; if (-gcw > v->bx) { guard_code |= MGL_CLIP_LEFT; } else if (v->bx > gcw) { guard_code |= MGL_CLIP_RIGHT; } if (-gcw > v->by) { guard_code |= MGL_CLIP_BOTTOM; } else if (v->by > gcw) { guard_code |= MGL_CLIP_TOP; } guard_band |= guard_code; } v++; } while (--i && guard_band == 0); return (int)guard_band; } } void A_ToScreenArray(GLcontext context, const int first, const int size) { int i, stride; float x,y,z,w; UBYTE *pointer; float *wa; float az; if(context->ZOffset_State == GL_TRUE) { az = context->az + context->ZOffset; } else { az = context->az; } if(!(context->ClientState & GLCS_TEXTURE)) { MGLVertex *v = &context->VertexBuffer[first]; i = size; do { x = v->bx; y = v->by; z = v->bz; w = 1.0 / v->bw; v->bx = context->ax + x * w * context->sx; v->by = context->ay - y * w * context->sy; v->bz = az + z * w * context->sz; v++; } while (--i); } else if(context->ArrayPointer.FixpointTrans == GL_TRUE) { MGLVertex *v = &context->VertexBuffer[first]; stride = context->ArrayPointer.texcoordstride; pointer = (UBYTE*)context->WBuffer + first * stride; i = size; do { x = v->bx; y = v->by; z = v->bz; w = 1.0 / v->bw; v->bx = context->ax + x * w * context->sx; v->by = context->ay - y * w * context->sy; v->bz = az + z * w * context->sz; w *= 32768.f; wa = (float*)pointer; *wa = w; v++; pointer += stride; } while (--i); } else { MGLVertex *v = &context->VertexBuffer[first]; stride = context->ArrayPointer.texcoordstride; pointer = (UBYTE*)context->WBuffer + first * stride; i = size; do { x = v->bx; y = v->by; z = v->bz; w = 1.0 / v->bw; v->bx = context->ax + x * w * context->sx; v->by = context->ay - y * w * context->sy; v->bz = az + z * w * context->sz; wa = (float*)pointer; *wa = w; v++; pointer += stride; } while (--i); } } //fix to float conversion INLINE void ConvertFixverts(GLcontext context, const int first, const int count) { const float f = 1.0/32768.f; MGLVertex *v = &context->VertexBuffer[first]; int i = count; do { v->bx = v->bx * f; v->by = v->by * f; v->bz = v->bz * f; v->bw = v->bw * f; v++; } while (--i); } // special case color-copy / texcoord-conversion routines void Convert_F_RGB(GLcontext context, int i) { MGLVertex *v; v = &context->VertexBuffer[i]; if(context->ClientState & GLCS_TEXTURE) { float *tex = (float*)(context->ArrayPointer.texcoords + i * context->ArrayPointer.texcoordstride); v->v.u = tex[0] * (float)context->w3dTexBuffer[context->CurrentBinding]->texwidth; v->v.v = tex[1] * (float)context->w3dTexBuffer[context->CurrentBinding]->texheight; } if(context->ShadeModel == GL_SMOOTH) { float *col = (float*)(context->ArrayPointer.colors + i * context->ArrayPointer.colorstride);; v->v.color.r = col[0]; v->v.color.g = col[1]; v->v.color.b = col[2]; v->v.color.a = 1.0; } } void Convert_F_RGBA(GLcontext context, int i) { MGLVertex *v; v = &context->VertexBuffer[i]; if(context->ClientState & GLCS_TEXTURE) { float *tex = (float*)(context->ArrayPointer.texcoords + i * context->ArrayPointer.texcoordstride); v->v.u = tex[0] * (float)context->w3dTexBuffer[context->CurrentBinding]->texwidth; v->v.v = tex[1] * (float)context->w3dTexBuffer[context->CurrentBinding]->texheight; } if(context->ShadeModel == GL_SMOOTH) { float *col = (float*)(context->ArrayPointer.colors + i * context->ArrayPointer.colorstride);; v->v.color.r = col[0]; v->v.color.g = col[1]; v->v.color.b = col[2]; v->v.color.a = col[3]; } } void Convert_UB_RGB(GLcontext context, int i) { MGLVertex *v; v = &context->VertexBuffer[i]; if(context->ClientState & GLCS_TEXTURE) { float *tex = (float*)(context->ArrayPointer.texcoords + i * context->ArrayPointer.texcoordstride); v->v.u = tex[0] * (float)context->w3dTexBuffer[context->CurrentBinding]->texwidth; v->v.v = tex[1] * (float)context->w3dTexBuffer[context->CurrentBinding]->texheight; } if(context->ShadeModel == GL_SMOOTH) { UBYTE *col = (context->ArrayPointer.colors + i * context->ArrayPointer.colorstride); float f = 1.0/255.0; v->v.color.r = (float)col[0]*f; v->v.color.g = (float)col[1]*f; v->v.color.b = (float)col[2]*f; v->v.color.a = 1.0; } } void Convert_UB_RGBA(GLcontext context, int i) { MGLVertex *v; v = &context->VertexBuffer[i]; if(context->ClientState & GLCS_TEXTURE) { float *tex = (float*)(context->ArrayPointer.texcoords + i * context->ArrayPointer.texcoordstride); v->v.u = tex[0] * (float)context->w3dTexBuffer[context->CurrentBinding]->texwidth; v->v.v = tex[1] * (float)context->w3dTexBuffer[context->CurrentBinding]->texheight; } if(context->ShadeModel == GL_SMOOTH) { UBYTE *col = (context->ArrayPointer.colors + i * context->ArrayPointer.colorstride); float f = 1.0/255.0; v->v.color.r = (float)col[0]*f; v->v.color.g = (float)col[1]*f; v->v.color.b = (float)col[2]*f; v->v.color.a = (float)col[3]*f; } } void Convert_UB_BGR(GLcontext context, int i) { MGLVertex *v; v = &context->VertexBuffer[i]; if(context->ClientState & GLCS_TEXTURE) { float *tex = (float*)(context->ArrayPointer.texcoords + i * context->ArrayPointer.texcoordstride); v->v.u = tex[0] * (float)context->w3dTexBuffer[context->CurrentBinding]->texwidth; v->v.v = tex[1] * (float)context->w3dTexBuffer[context->CurrentBinding]->texheight; } if(context->ShadeModel == GL_SMOOTH) { UBYTE *col = (context->ArrayPointer.colors + i * context->ArrayPointer.colorstride); float f = 1.0/255.0; v->v.color.b = (float)col[0]*f; v->v.color.g = (float)col[1]*f; v->v.color.r = (float)col[2]*f; v->v.color.a = 1.0; } } void Convert_UB_BGRA(GLcontext context, int i) { MGLVertex *v; v = &context->VertexBuffer[i]; if(context->ClientState & GLCS_TEXTURE) { float *tex = (float*)(context->ArrayPointer.texcoords + i * context->ArrayPointer.texcoordstride); v->v.u = tex[0] * (float)context->w3dTexBuffer[context->CurrentBinding]->texwidth; v->v.v = tex[1] * (float)context->w3dTexBuffer[context->CurrentBinding]->texheight; } if(context->ShadeModel == GL_SMOOTH) { UBYTE *col = (context->ArrayPointer.colors + i * context->ArrayPointer.colorstride); float f = 1.0/255.0; v->v.color.b = (float)col[0]*f; v->v.color.g = (float)col[1]*f; v->v.color.r = (float)col[2]*f; v->v.color.a = (float)col[3]*f; } } void Convert_UB_ARGB(GLcontext context, int i) { MGLVertex *v; v = &context->VertexBuffer[i]; if(context->ClientState & GLCS_TEXTURE) { float *tex = (float*)(context->ArrayPointer.texcoords + i * context->ArrayPointer.texcoordstride); v->v.u = tex[0] * (float)context->w3dTexBuffer[context->CurrentBinding]->texwidth; v->v.v = tex[1] * (float)context->w3dTexBuffer[context->CurrentBinding]->texheight; } if(context->ShadeModel == GL_SMOOTH) { UBYTE *col = (context->ArrayPointer.colors + i * context->ArrayPointer.colorstride); float f = 1.0/255.0; v->v.color.a = (float)col[0]*f; v->v.color.r = (float)col[1]*f; v->v.color.g = (float)col[2]*f; v->v.color.b = (float)col[3]*f; } } GLboolean DecideFrontface(GLcontext context, const MGLVertex *a, const MGLVertex *b, const MGLVertex *c, int sign) { float a1,a2,b1,b2,r; float aw,bw,cw; aw = 1.0 / a->bw; bw = 1.0 / b->bw; cw = 1.0 / c->bw; a1 = a->bx*aw - b->bx*bw; a2 = a->by*aw - b->by*bw; b1 = c->bx*cw - b->bx*bw; b2 = c->by*cw - b->by*bw; r = (a1*b2-a2*b1); #if 0 //this test eliminates less than 1 in 1000 triangles #define EPSILON (1e-5) if (fabs(a1) < EPSILON && fabs(a2) < EPSILON) { return GL_FALSE; } if (fabs(b1) < EPSILON && fabs(b2) < EPSILON) { return GL_FALSE; } #endif if((sign < 0 && r < 0.0) || (sign > 0 && r > 0.0)) return GL_FALSE; else return GL_TRUE; } INLINE void A_ToScreen(GLcontext context, int vnum) { UBYTE *pointer; float *wa; float w = 1.0/context->VertexBuffer[vnum].bw; context->VertexBuffer[vnum].bx = context->ax + context->VertexBuffer[vnum].bx * w * context->sx; context->VertexBuffer[vnum].by = context->ay - context->VertexBuffer[vnum].by * w * context->sy; context->VertexBuffer[vnum].bz = context->az + context->VertexBuffer[vnum].bz * w * context->sz; if (context->ZOffset_State == GL_TRUE) context->VertexBuffer[vnum].bz += (W3D_Float)context->ZOffset; pointer = (UBYTE*)context->WBuffer + vnum * context->ArrayPointer.texcoordstride; wa = (float*)pointer; *wa = w; } //avoid double projections: #ifdef __VBCC__ #define A_ToV(ctx, e) {\ MGLVertex *v = &ctx->VertexBuffer[e];\ v->v.x = v->bx;\ v->v.y = v->by;\ v->v.z = v->bz;\ v->v.w = 1.0 / v->bw; \ } #define V_ToScreen(ctx,vnum){\ static float wdiv;\ wdiv = 1.0 / ctx->VertexBuffer[vnum].bw; \ ctx->VertexBuffer[vnum].v.x = ctx->ax + ctx->VertexBuffer[vnum].bx * wdiv * ctx->sx; \ ctx->VertexBuffer[vnum].v.y = ctx->ay - ctx->VertexBuffer[vnum].by * wdiv * ctx->sy; \ ctx->VertexBuffer[vnum].v.z = ctx->az + ctx->VertexBuffer[vnum].bz * wdiv * ctx->sz; \ if (ctx->ZOffset_State == GL_TRUE) ctx->VertexBuffer[vnum].v.z += (W3D_Float)ctx->ZOffset; \ ctx->VertexBuffer[vnum].v.w = wdiv; \ } #else static INLINE void A_ToV(GLcontext context, int e) { context->VertexBuffer[e].v.x = (W3D_Float)context->VertexBuffer[e].bx; context->VertexBuffer[e].v.y = (W3D_Float)context->VertexBuffer[e].by; context->VertexBuffer[e].v.z = (W3D_Float)context->VertexBuffer[e].bz; context->VertexBuffer[e].v.w = (W3D_Float)(1.0 / context->VertexBuffer[e].bw); } static INLINE void V_ToScreen(GLcontext context, int vnum) { GLfloat wdiv = 1.f / context->VertexBuffer[vnum].bw; context->VertexBuffer[vnum].v.x = (W3D_Float)(context->ax + context->VertexBuffer[vnum].bx * wdiv * context->sx); context->VertexBuffer[vnum].v.y = (W3D_Float)(context->ay - context->VertexBuffer[vnum].by * wdiv * context->sy); context->VertexBuffer[vnum].v.z = (W3D_Float)(context->az + context->VertexBuffer[vnum].bz * wdiv * context->sz); if (context->ZOffset_State == GL_TRUE) context->VertexBuffer[vnum].v.z += (W3D_Float)context->ZOffset; \ context->VertexBuffer[vnum].v.w = (W3D_Float)wdiv; \ } #endif void A_DrawPoints(GLcontext context, const int first, const int count) { } void A_DrawLines(GLcontext context, const int first, const int count) { } void A_DrawLineStrip(GLcontext context, const int first, const int count) { } void A_DrawLineLoop(GLcontext context, const int first, const int count) { } void A_DrawQuads(GLcontext context, const int first, const int count) { } void A_DrawQuadStrip(GLcontext context, const int first, const int count) { } void A_DrawTriFan(GLcontext context, const int first, const int count) { int i,j; int size; int outcode; ULONG local_or, local_and; ULONG error; static W3D_Vertex **verts = NULL; static W3D_TrianglesV fan; static ULONG complete[MGL_MAXVERTS]; static GLboolean visible[MGL_MAXVERTS]; int triangle; static PolyBuffer polys[64]; PolyBuffer clip[64]; int pnum, cnum, backface, prevcopy, free; int sign; if(Clip_Volume_Bypass != GL_FALSE) { //compiled arrays size = count; //first check if we are in guardband-mode //and discard/shrink offscreen primitives if(Clip_Volume_Bypass == GL_TRUE + 1) { i = first + size - 2; do { local_and = context->VertexBuffer[first].outcode & context->VertexBuffer[i].outcode & context->VertexBuffer[i+1].outcode; if(local_and == 0) break; size--; i--; } while (i > first); if(size < 3) return; } if(context->CullFace_State == GL_FALSE) { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIFAN, first, size); } else { int newfirst; float fsign; float area; float x1,x2; float y1,y2; #define x(a) (context->VertexBuffer[first+a].bx) #define y(a) (context->VertexBuffer[first+a].by) fsign = (float)-context->CurrentCullSign; backface = 0; newfirst = 0; if(size == 3) { x1 = x(1) - x(0); y1 = y(1) - y(0); x2 = x(2) - x(0); y2 = y(2) - y(0); area = y2*x1 - x2*y1; area *= fsign; } else { float x0,y0; float area2; x0 = x(0); y0 = y(0); x1 = x(1) - x0; y1 = y(1) - y0; x2 = x(2) - x0; y2 = y(2) - y0; area = y2*x1 - x2*y1; area *= fsign; if(area < 0.f) { newfirst++; area = 0.f; } i = 1; do { x1 = x2; y1 = y2; x2 = x(i+2) - x0; y2 = y(i+2) - y0; area2 = y2*x1 - x2*y1; area2 *= fsign; if(area2 < 0.f) { if(newfirst == i) newfirst++; else backface++; } else { backface = 0; area += area2; } i++; } while (i < size-2); } if(area < context->MinTriArea) return; size -= backface; //cut end if( newfirst ) { size -= newfirst; newfirst += first; e_wrap[newfirst] = first; error = W3D_DrawElements(context->w3dContext, W3D_PRIMITIVE_TRIFAN, W3D_INDEX_UWORD, size, (void*)&(e_wrap[newfirst])); e_wrap[newfirst] = newfirst; } else { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIFAN, first, size); } #undef x #undef y } return; } sign = context->CurrentCullSign; outcode = A_TransformArray(context, first, count); if(outcode < 0) { return; } if (outcode == 0) { size = count; A_ToScreenArray(context, first, count); if(context->CullFace_State == GL_TRUE) { int newfirst; float fsign; float area; float x1,x2; float y1,y2; #define x(a) (context->VertexBuffer[first+a].bx) #define y(a) (context->VertexBuffer[first+a].by) fsign = (float)-sign; backface = 0; newfirst = 0; if(size == 3) { x1 = x(1) - x(0); y1 = y(1) - y(0); x2 = x(2) - x(0); y2 = y(2) - y(0); area = y2*x1 - x2*y1; area *= fsign; } else { float x0,y0; float area2; x0 = x(0); y0 = y(0); x1 = x(1) - x0; y1 = y(1) - y0; x2 = x(2) - x0; y2 = y(2) - y0; area = y2*x1 - x2*y1; area *= fsign; if(area < 0.f) { newfirst++; area = 0.f; } i = 1; do { x1 = x2; y1 = y2; x2 = x(i+2) - x0; y2 = y(i+2) - y0; area2 = y2*x1 - x2*y1; area2 *= fsign; if(area2 < 0.f) { if(newfirst == i) newfirst++; else backface++; } else { backface = 0; area += area2; } i++; } while (i < size-2); } if(area < context->MinTriArea) return; size -= backface; //cut end if( newfirst ) { size -= newfirst; newfirst += first; e_wrap[newfirst] = first; error = W3D_DrawElements(context->w3dContext, W3D_PRIMITIVE_TRIFAN, W3D_INDEX_UWORD, size, (void*)&(e_wrap[newfirst])); e_wrap[newfirst] = newfirst; return; } #undef x #undef y } error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIFAN, first, size); return; } if (verts == NULL) { verts = (W3D_Vertex **)malloc(sizeof(W3D_Vertex *) * context->VertexBufferSize); if (!verts) return; } if(context->ArrayPointer.FixpointTrans == GL_TRUE) { ConvertFixverts(context, first, count); } backface = 0; triangle = 0; i = first+1; if(context->CullFace_State == GL_FALSE) { do { local_and = context->VertexBuffer[first].outcode & context->VertexBuffer[i].outcode & context->VertexBuffer[i+1].outcode; local_or = context->VertexBuffer[first].outcode | context->VertexBuffer[i].outcode | context->VertexBuffer[i+1].outcode; if (local_and == 0) // if the local and code is zero, we're not { complete[triangle] = local_or; visible[triangle] = GL_TRUE; backface = 0; } else { visible[triangle] = GL_FALSE; backface++; } i++; triangle++; } while (triangle < count - 2); } else { do { local_and = context->VertexBuffer[first].outcode & context->VertexBuffer[i].outcode & context->VertexBuffer[i+1].outcode; local_or = context->VertexBuffer[first].outcode | context->VertexBuffer[i].outcode | context->VertexBuffer[i+1].outcode; if (local_and == 0) // if the local and code is zero, we're not { complete[triangle] = local_or; if(local_or & MGL_CLIP_NEGW) { visible[triangle] = GL_TRUE; backface = 0; } else { visible[triangle] = DecideFrontface(context, &(context->VertexBuffer[first]), &(context->VertexBuffer[i]), &(context->VertexBuffer[i+1]), sign); if(!visible[triangle]) backface++; else backface = 0; } } else { visible[triangle] = GL_FALSE; backface++; } i++; triangle++; } while (triangle < count - 2); } if(backface == triangle) //early out return; size = count - backface; context->VertexBufferPointer -= backface; Convert(context, first); prevcopy = 0; pnum = 0; cnum = 0; free = context->VertexBufferPointer; triangle = 0; i = first + 1; do { if (visible[triangle] == GL_FALSE) // case 3 { triangle ++; i ++; } else { if (complete[triangle]) // case 1 { clip[cnum].numverts = 3; clip[cnum].verts[0] = first; clip[cnum].verts[1] = i + 0; clip[cnum].verts[2] = i + 1; if(prevcopy != i) Convert(context, i+0); Convert(context, i+1); prevcopy = i+1; AE_ClipPoly(context, &clip[cnum], free, complete[triangle]); if(clip[cnum].numverts) { free = clip[cnum].nextfree; cnum++; } triangle++; i++; } else { // case 2 (the difficult part) int k=3; polys[pnum].verts[0] = first; polys[pnum].verts[1] = i+0; polys[pnum].verts[2] = i+1; triangle++; i++; while (complete[triangle]==0 && visible[triangle] && triangle < size - 2) { polys[pnum].verts[k] = i+1; i++; k++; triangle++; } polys[pnum].numverts = k; pnum++; } } } while (triangle < size - 2); //Project to screen and draw: //avoid some double-projections if(cnum) { for(i=0; i<size; i++) visible[first+i] = 0; //project clipped verts without index lookup for(i=context->VertexBufferPointer; i<free; i++) { V_ToScreen(context, i); } } if(pnum) //draw buffered unclipped trifans { PolyBuffer *p; p = &polys[0]; i = p->verts[1]; A_ToScreen(context, first); A_ToScreen(context, i); A_ToScreen(context, i+1); visible[first] = 1; visible[i] = 1; visible[i+1] = 1; if(p->numverts > 3) { int last = p->verts[p->numverts-1]; i+=2; do { A_ToScreen(context, i); visible[i] = 1; i++; } while (i <= last); } if(p->verts[1] == (first+1)) { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIFAN, first, p->numverts); } else { error = W3D_DrawElements(context->w3dContext, W3D_PRIMITIVE_TRIFAN, W3D_INDEX_ULONG, p->numverts, (void*)p->verts); } j = 1; while (j < pnum) { p = &polys[j]; i = p->verts[1]; A_ToScreen(context, i); A_ToScreen(context, i+1); visible[i] = 1; visible[i+1] = 1; if(p->numverts > 3) { int last = p->verts[p->numverts-1]; i+=2; do { A_ToScreen(context, i); visible[i] = 1; i++; } while (i <= last); } error = W3D_DrawElements(context->w3dContext, W3D_PRIMITIVE_TRIFAN, W3D_INDEX_ULONG, p->numverts, (void*)p->verts); j++; } } if(cnum) //draw clipped triangles { int first = 0; fan.tex = context->w3dTexBuffer[context->CurrentBinding]; fan.st_pattern = 0; j = 0; do { PolyBuffer *p = &clip[j]; for (i=0; i<p->numverts; i++) { int vert = p->verts[i]; if(vert < context->VertexBufferPointer) { switch(visible[vert]) { case 2: break; case 1: A_ToV(context, vert); visible[vert] = 2; break; default: V_ToScreen(context, vert); visible[vert] = 2; break; } } verts[i+first] = &(context->VertexBuffer[vert].v); } fan.vertexcount = p->numverts; fan.v = &verts[first]; error = W3D_DrawTriFanV(context->w3dContext, &fan); first += p->numverts; j++; } while (j < cnum); } } void A_DrawTriStrip(GLcontext context, const int first, const int count) { int i,j; int size; int outcode; ULONG local_or, local_and; ULONG error; static W3D_Vertex **verts = NULL; static W3D_TrianglesV fan; static GLboolean visible[MGL_MAXVERTS]; static ULONG complete[MGL_MAXVERTS]; int triangle; static PolyBuffer polys[64]; PolyBuffer clip[64]; int pnum, cnum, backface, free, prevcopy; int sign; if(Clip_Volume_Bypass != GL_FALSE) { //compiled arrays size = count; //first check if we are in guardband-mode //and discard/shrink offscreen primitives if(Clip_Volume_Bypass == GL_TRUE + 1) { i = first + size - 3; do { local_and = context->VertexBuffer[i].outcode & context->VertexBuffer[i+1].outcode & context->VertexBuffer[i+2].outcode; if(local_and == 0) break; size--; i--; } while (i >= first); if(size < 3) return; } if(context->CullFace_State == GL_FALSE) { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRISTRIP, first, size); } else { int newfirst; float fsign; float area; float x1,x2; float y1,y2; #define x(a) (context->VertexBuffer[first+a].bx) #define y(a) (context->VertexBuffer[first+a].by) fsign = (float)-context->CurrentCullSign; backface = 0; newfirst = 0; if(size == 3) { x1 = x(1) - x(0); y1 = y(1) - y(0); x2 = x(2) - x(0); y2 = y(2) - y(0); area = y2*x1 - x2*y1; area *= fsign; } else { float a1,a2,b1,b2; float area2; a1 = x(1); b1 = y(1); a2 = x(2); b2 = y(2); x1 = a1 - x(0); y1 = b1 - y(0); x2 = a2 - x(0); y2 = b2 - y(0); area = y2*x1 - x2*y1; area *= fsign; if(area < 0.f) { newfirst++; area = 0.f; } i = 1; do { fsign = -fsign; x1 = a2 - a1; y1 = b2 - b1; x2 = x(i+2) - a1; y2 = y(i+2) - b1; a1 = a2; b1 = b2; a2 = x(i+2); b2 = y(i+2); area2 = y2*x1 - x2*y1; area2 *= fsign; if(area2 < 0.f) { if(newfirst == i) newfirst++; else backface++; } else { backface = 0; area += area2; } i++; } while (i < size-2); } if(area < context->MinTriArea) return; size -= newfirst + backface; newfirst += first; error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRISTRIP, newfirst, size); #undef x #undef y } return; } sign = context->CurrentCullSign; outcode = A_TransformArray(context, first, count); if (outcode < 0) { return; } if (outcode == 0) { int newfirst; size = count; A_ToScreenArray(context, first, count); if(context->CullFace_State == GL_FALSE) { newfirst = first; } else { #define x(a) (context->VertexBuffer[first+a].bx) #define y(a) (context->VertexBuffer[first+a].by) float fsign; float area; float x1,x2; float y1,y2; fsign = (float)-sign; backface = 0; newfirst = 0; if(size == 3) { x1 = x(1) - x(0); y1 = y(1) - y(0); x2 = x(2) - x(0); y2 = y(2) - y(0); area = y2*x1 - x2*y1; area *= fsign; } else { float a1,a2,b1,b2; float area2; a1 = x(1); b1 = y(1); a2 = x(2); b2 = y(2); x1 = a1 - x(0); y1 = b1 - y(0); x2 = a2 - x(0); y2 = b2 - y(0); area = y2*x1 - x2*y1; area *= fsign; if(area < 0.f) { newfirst++; area = 0.f; } i = 1; do { fsign = -fsign; x1 = a2 - a1; y1 = b2 - b1; x2 = x(i+2) - a1; y2 = y(i+2) - b1; a1 = a2; b1 = b2; a2 = x(i+2); b2 = y(i+2); area2 = y2*x1 - x2*y1; area2 *= fsign; if(area2 < 0.f) { if(newfirst == i) newfirst++; else backface++; } else { backface = 0; area += area2; } i++; } while (i < size-2); } if(area < context->MinTriArea) return; size -= newfirst + backface; newfirst += first; #undef x #undef y } error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRISTRIP, newfirst, size); return; } if (verts == NULL) { verts = (W3D_Vertex **)malloc(sizeof(W3D_Vertex *) * context->VertexBufferSize); if (!verts) return; } if(context->ArrayPointer.FixpointTrans == GL_TRUE) { ConvertFixverts(context, first, count); } backface = 0; triangle = 0; i = first; if(context->CullFace_State == GL_FALSE) { do { local_and = context->VertexBuffer[i+0].outcode & context->VertexBuffer[i+1].outcode & context->VertexBuffer[i+2].outcode; local_or = context->VertexBuffer[i+0].outcode | context->VertexBuffer[i+1].outcode | context->VertexBuffer[i+2].outcode; if (local_and == 0) { complete[triangle] = local_or; visible[triangle] = GL_TRUE; backface = 0; } else { visible[triangle] = GL_FALSE; backface++; } i++; triangle++; } while (triangle < count - 2); } else { do { local_and = context->VertexBuffer[i+0].outcode & context->VertexBuffer[i+1].outcode & context->VertexBuffer[i+2].outcode; local_or = context->VertexBuffer[i+0].outcode | context->VertexBuffer[i+1].outcode | context->VertexBuffer[i+2].outcode; if (local_and == 0) { complete[triangle] = local_or; if(local_or & MGL_CLIP_NEGW) { visible[triangle] = GL_TRUE; backface = 0; } else { visible[triangle] = DecideFrontface(context, &(context->VertexBuffer[i+0]), &(context->VertexBuffer[i+1]), &(context->VertexBuffer[i+2]), sign); if(!visible[triangle]) backface++; else backface = 0; } } else { visible[triangle] = GL_FALSE; backface++; } sign = -sign; //reverse order i++; triangle++; } while (triangle < count - 2); } if(backface == triangle) //early out return; size = count - backface; context->VertexBufferPointer -= backface; prevcopy = -1; pnum = 0; cnum = 0; free = context->VertexBufferPointer; triangle = 0; i=first; do { if (visible[triangle] == GL_FALSE) // case 3 { triangle++; i++; } else { if (complete[triangle]) // case 1 { clip[cnum].numverts = 3; clip[cnum].verts[0] = i + 0; clip[cnum].verts[1] = i + 1; clip[cnum].verts[2] = i + 2; //this element is never shared with next triangle: if(prevcopy == i+1) { Convert(context, i+2); } else if(prevcopy == i) { Convert(context, i+1); Convert(context, i+2); } else { Convert(context, i+0); Convert(context, i+1); Convert(context, i+2); } //this element is never shared with previous triangle: prevcopy = i+2; AE_ClipPoly(context, &clip[cnum], free, complete[triangle]); if(clip[cnum].numverts) { free = clip[cnum].nextfree; cnum++; } triangle++; i++; } else { // case 2 (the difficult part) int k=3; polys[pnum].verts[0] = i+0; //polys[pnum].verts[1] = i+1; //polys[pnum].verts[2] = i+2; triangle++; i++; while (visible[triangle] && complete[triangle]==0 && triangle < size - 2 && k < 64) { //polys[pnum].verts[k] = i+2; i++; k++; triangle++; } polys[pnum].numverts = k; pnum++; } } } while (triangle < size - 2); //Project to screen and Draw: //avoid some double-projections if(cnum) { for(i=0; i < size; i++) visible[first+i] = 0; //project clipped verts without need for index lookup for(i=context->VertexBufferPointer; i<free; i++) { V_ToScreen(context, i); } } if(pnum) //draw buffered unclipped tristrips { PolyBuffer *p; p = &polys[0]; i = p->verts[0]; A_ToScreen(context, i); A_ToScreen(context, i+1); A_ToScreen(context, i+2); visible[i] = 1; visible[i+1] = 1; visible[i+2] = 1; if(p->numverts > 3) { int last = p->numverts + i; i+=3; do { A_ToScreen(context, i); visible[i] = 1; i++; } while (i < last); } error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRISTRIP, p->verts[0], p->numverts); j = 1; while (j < pnum) { int prev_projected = p->verts[0] + p->numverts-1; p = &polys[j]; i = p->verts[0]; if (i != prev_projected) { A_ToScreen(context, i); visible[i] = 1; } A_ToScreen(context, i+1); A_ToScreen(context, i+2); visible[i+1] = 1; visible[i+2] = 1; if(p->numverts > 3) { int last = i + p->numverts; i+=3; do { A_ToScreen(context, i); visible[i] = 1; i++; } while (i < last); } error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRISTRIP, p->verts[0], p->numverts); j++; } } if(cnum) //draw clipped triangles { int first = 0; fan.tex = context->w3dTexBuffer[context->CurrentBinding]; fan.st_pattern = 0; j = 0; do { PolyBuffer *p = &clip[j]; for (i=0; i<p->numverts; i++) { int vert = p->verts[i]; if(vert < context->VertexBufferPointer) { switch(visible[vert]) { case 2: break; case 1: A_ToV(context, vert); visible[vert] = 2; break; default: V_ToScreen(context, vert); visible[vert] = 2; break; } } verts[i+first] = &(context->VertexBuffer[vert].v); } fan.vertexcount = p->numverts; fan.v = &verts[first]; error = W3D_DrawTriFanV(context->w3dContext, &fan); first += p->numverts; j++; } while (j < cnum); } } void A_DrawTriangles(GLcontext context, const int first, const int count) { int i,j; int outcode; static ULONG complete; ULONG error; static W3D_Vertex **verts = NULL; static GLboolean visible; PolyBuffer clip[128]; //should be enough static int sign; int cnum, free, start; static int chainverts; if(Clip_Volume_Bypass != GL_FALSE) { //compiled arrays if(context->CullFace_State == GL_FALSE) { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIANGLES, first, count); } else { int start; float area; float x1,y1; float x2,y2; #define x(i) (context->VertexBuffer[first+i].bx) #define y(i) (context->VertexBuffer[first+i].by) start = 0; chainverts = 0; sign = context->CurrentCullSign; for(i=0; i<count; i+=3) { if(Clip_Volume_Bypass == GL_TRUE + 1) { if(context->VertexBuffer[first+i].outcode & context->VertexBuffer[first+i+1].outcode & context->VertexBuffer[first+i+2].outcode) { if(chainverts) { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIANGLES, first + start, chainverts); chainverts = 0; } continue; } } x1 = x(i+1) - x(i); y1 = y(i+1) - y(i); x2 = x(i+2) - x(i); y2 = y(i+2) - y(i); area = y2*x1 - x2*y1; if(sign > 0) area = -area; if(area < context->MinTriArea) { if(chainverts) { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIANGLES, first + start, chainverts); chainverts = 0; } } else { if(chainverts == 0) { start = i; } chainverts += 3; } } if(chainverts) //draw remainder { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIANGLES, first + start, chainverts); } #undef x #undef y } return; } if(verts == NULL) { verts = (W3D_Vertex **)malloc(sizeof(W3D_Vertex *) * context->VertexBufferSize); if (!verts) return; } sign = context->CurrentCullSign; chainverts = 0; cnum = 0; free = context->VertexBufferPointer; start = first; i = first; do { outcode = A_TransformArray(context, i, 3); if(outcode < 0) { if(chainverts) { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIANGLES, start, chainverts); chainverts = 0; } i+=3; } else { complete = (ULONG)outcode; if(complete == 0) { A_ToScreenArray(context, i, 3); if(context->CullFace_State == GL_FALSE) { visible = GL_TRUE; } else { #define x(a) (context->VertexBuffer[a].bx) #define y(a) (context->VertexBuffer[a].by) float area; float x1,x2,y1,y2; x1 = x(i+1) - x(i); y1 = y(i+1) - y(i); x2 = x(i+2) - x(i); y2 = y(i+2) - y(i); area = y2*x1 - x2*y1; if(sign > 0) area = -area; if(area < context->MinTriArea) visible = GL_FALSE; else visible = GL_TRUE; #undef x #undef y } if(visible) { if(chainverts == 0) { start = i; chainverts = 3; } else { chainverts += 3; } } else if (chainverts) { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIANGLES, start, chainverts); chainverts = 0; } i+=3; } else { if(chainverts) { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIANGLES, start, chainverts); chainverts = 0; } if(context->ArrayPointer.FixpointTrans == GL_TRUE) { ConvertFixverts(context, i, 3); } if(context->CullFace_State == GL_FALSE || (complete & MGL_CLIP_NEGW)) { visible = GL_TRUE; } else { visible = DecideFrontface(context, &(context->VertexBuffer[i+0]), &(context->VertexBuffer[i+1]), &(context->VertexBuffer[i+2]), sign); } if(visible) { Convert(context, i+0); Convert(context, i+1); Convert(context, i+2); clip[cnum].numverts = 3; clip[cnum].verts[0] = i+0; clip[cnum].verts[1] = i+1; clip[cnum].verts[2] = i+2; AE_ClipPoly(context, &clip[cnum], free, complete); if(clip[cnum].numverts) { free = clip[cnum].nextfree; cnum++; } } i+=3; } } } while (i < first + count); if(chainverts) //draw remainder of unclipped triangles { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIANGLES, start, chainverts); } if (cnum) { static W3D_TrianglesV fan; static PolyBuffer *p; static int start; fan.st_pattern = 0; fan.tex = context->w3dTexBuffer[context->CurrentBinding]; start = 0; i = 0; do { p = &clip[i]; j = 0; while (j < p->numverts) { verts[start+j] = &(context->VertexBuffer[p->verts[j]].v); V_ToScreen(context, p->verts[j]); j++; } fan.v = &verts[start]; fan.vertexcount = p->numverts; start += p->numverts; error = W3D_DrawTriFanV(context->w3dContext, &fan); i++; } while (i < cnum); } } #if 0 void A_DrawTriangle(GLcontext context, const int first) { int i; int outcode; ULONG complete; ULONG error; static W3D_Vertex *verts[16]; static W3D_TrianglesV fan; PolyBuffer clip; if(Clip_Volume_Bypass != GL_FALSE) { if(context->CullFace_State == GL_FALSE) { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIANGLES, first, 3); } else { float area; float x1,y1; float x2,y2; #define x(i) (context->VertexBuffer[first+i].bx) #define y(i) (context->VertexBuffer[first+i].by) x1 = x(1) - x(0); y1 = y(1) - y(0); x2 = x(2) - x(0); y2 = y(2) - y(0); area = y2*x1 - x2*y1; if(context->CurrentCullSign > 0) area = -area; if(area > context->MinTriArea) { error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIANGLES, first, 3); } #undef x #undef y } return; } outcode = A_TransformArray(context, first, 3); if(outcode < 0) { return; } else if (outcode == 0) { A_ToScreenArray(context, first, 3); if(context->CullFace_State == GL_TRUE) { float area; float x1,y1; float x2,y2; #define x(i) (context->VertexBuffer[first+i].bx) #define y(i) (context->VertexBuffer[first+i].by) x1 = x(1) - x(0); y1 = y(1) - y(0); x2 = x(2) - x(0); y2 = y(2) - y(0); area = y2*x1 - x2*y1; if(context->CurrentCullSign > 0) area = -area; if(area < context->MinTriArea) return; #undef x #undef y } error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIANGLES, first, 3); return; } complete = (ULONG)outcode; if(context->CullFace_State == GL_TRUE && !(complete & MGL_CLIP_NEGW)) { if(DecideFrontface(context, &(context->VertexBuffer[first]), &(context->VertexBuffer[first+1]), &(context->VertexBuffer[first+2]), context->CurrentCullSign) == GL_FALSE) return; } Convert(context, first); Convert(context, first+1); Convert(context, first+2); clip.numverts = 3; clip.verts[0] = first; clip.verts[1] = first+1; clip.verts[2] = first+2; AE_ClipPoly(context, &clip, context->VertexBufferPointer, complete); if(clip.numverts < 3) return; i = 0; do { verts[i] = &(context->VertexBuffer[clip.verts[i]].v); V_ToScreen(context, clip.verts[i]); i++; } while (i < clip.numverts); fan.st_pattern = 0; fan.tex = context->w3dTexBuffer[context->CurrentBinding]; fan.v = verts; fan.vertexcount = clip.numverts; error = W3D_DrawTriFanV(context->w3dContext, &fan); } #endif void A_DrawPolygon(GLcontext context, const int first, const int count) { int i,j; int outcode; ULONG complete; ULONG error; GLboolean visible; PolyBuffer clip; static W3D_Vertex **verts = NULL; static W3D_TrianglesV fan; if(Clip_Volume_Bypass != GL_FALSE) { //compiled arrays if(Clip_Volume_Bypass == GL_TRUE + 1) { ULONG and_code; and_code = 0xff; for(i=first; i<first+count; i++) and_code &= context->VertexBuffer[i].outcode; if(and_code) return; } if(context->CullFace_State == GL_TRUE) { float x0,y0; float x1,y1; float x2,y2; GLfloat area; #define x(a) (context->VertexBuffer[first+a].bx) #define y(a) (context->VertexBuffer[first+a].by) x0 = x(0); y0 = y(0); x1 = x(1) - x0; y1 = y(1) - y0; x2 = x(2) - x0; y2 = y(2) - y0; area = y2*x1 - x2*y1; i = 1; while (i < count-2 && fabs(area) < context->MinTriArea) { x1 = x2; y1 = y2; x2 = x(i+2) - x0; y2 = y(i+2) - y0; area += y2*x1 - x2*y1; i++; } if(context->CurrentCullSign > 0) area = -area; if(area < context->MinTriArea) return; #undef x #undef y } error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIFAN, first, count); return; } outcode = A_TransformArray(context, first, count); if(outcode < 0) return; else if(outcode == 0) { A_ToScreenArray(context, first, count); if(context->CullFace_State == GL_TRUE) { float x0,y0; float x1,y1; float x2,y2; GLfloat area; #define x(a) (context->VertexBuffer[first+a].bx) #define y(a) (context->VertexBuffer[first+a].by) x0 = x(0); y0 = y(0); x1 = x(1) - x0; y1 = y(1) - y0; x2 = x(2) - x0; y2 = y(2) - y0; area = y2*x1 - x2*y1; i = 1; while (i < count-2 && fabs(area) < context->MinTriArea) { x1 = x2; y1 = y2; x2 = x(i+2) - x0; y2 = y(i+2) - y0; area += y2*x1 - x2*y1; i++; } if(context->CurrentCullSign > 0) area = -area; if(area < context->MinTriArea) return; #undef x #undef y } error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIFAN, first, count); } else { complete = (ULONG)outcode; if(verts == NULL) { verts = (W3D_Vertex **)malloc(sizeof(W3D_Vertex *) * context->VertexBufferSize); if (!verts) return; } if(context->ArrayPointer.FixpointTrans == GL_TRUE) { ConvertFixverts(context, first, count); } i = first; j = 0; while (i < first + count) { Convert(context, i); clip.verts[j++] = i; i++; } clip.numverts = count; AE_ClipPoly(context, &clip, context->VertexBufferPointer, complete); if(clip.numverts < 3) return; i = 0; do { verts[i] = &(context->VertexBuffer[clip.verts[i]].v); V_ToScreen(context, clip.verts[i]); i++; } while (i < clip.numverts); if(context->CullFace_State == GL_TRUE) { float x0,y0; float x1,y1; float x2,y2; GLfloat area; #define x(a) (context->VertexBuffer[clip.verts[a]].bx) #define y(a) (context->VertexBuffer[clip.verts[a]].by) x0 = x(0); y0 = y(0); x1 = x(1) - x0; y1 = y(1) - y0; x2 = x(2) - x0; y2 = y(2) - y0; area = y2*x1 - x2*y1; i = 1; while (i < clip.numverts-2 && fabs(area) < context->MinTriArea) { x1 = x2; y1 = y2; x2 = x(i+2) - x0; y2 = y(i+2) - y0; area += y2*x1 - x2*y1; i++; } if(context->CurrentCullSign > 0) area = -area; if(area < context->MinTriArea) return; #undef x #undef y } fan.tex = context->w3dTexBuffer[context->CurrentBinding]; fan.st_pattern = 0; fan.v = verts; fan.vertexcount = clip.numverts; error = W3D_DrawTriFanV(context->w3dContext, &fan); } } void A_DrawFlatFan(GLcontext context, const int first, const int count) { int i; ULONG error; MGLVertex *v; int Vstride; UBYTE *Vpointer; int Wstride; UBYTE *Wpointer; float *W; Vstride = context->ArrayPointer.vertexstride; Wstride = context->ArrayPointer.texcoordstride; Wpointer = (UBYTE*)context->WBuffer + first * Wstride; Vpointer = context->ArrayPointer.verts + first * Vstride; v = &context->VertexBuffer[first]; if(context->ArrayPointer.FixpointTrans == GL_TRUE) { int *V; i = count; do { V = (int*)Vpointer; v->bx = (float)V[0]; v->by = (float)V[1]; v->bz = (float)V[2]; if(context->ClientState & GLCS_TEXTURE) { W = (float*)Wpointer; *W = 1.0; } v++; Vpointer += Vstride; Wpointer += Wstride; } while (--i); } else { float *V; i = count; do { V = (float*)Vpointer; v->bx = V[0]; v->by = V[1]; v->bz = V[2]; if(context->ClientState & GLCS_TEXTURE) { W = (float*)Wpointer; *W = 1.0; } v++; Vpointer += Vstride; Wpointer += Wstride; } while (--i); } error = W3D_DrawArray(context->w3dContext, W3D_PRIMITIVE_TRIFAN, first, count); } //end of pipeline #ifdef VA_SANITY_CHECK // Swap_TextureCoordPointers added 01-06-2002 // Thanks to Olivier Fabre for making me aware of possible // memprot problems without such a safety-check. // It will only be needed if glTexcoordPointer, // GL_TEXTURE_COORD_ARRAY and GL_TEXTURE_2D state are not // in "sync". void Swap_TextureCoordPointers(GLcontext context, GLboolean enable) { if(enable == GL_TRUE) { Set_W3D_TexCoordPointer(context->w3dContext, (void*)context->ArrayPointer.texcoords, context->ArrayPointer.texcoordstride, 0, sizeof(GLfloat), context->ArrayPointer.w_off, W3D_TEXCOORD_NORMALIZED); } else { // guaranteed valid spot as long as vertexbuffer // is big enough Set_W3D_TexCoordPointer(context->w3dContext, (void*)&context->VertexBuffer[0].v.u, sizeof(MGLVertex), 0, 4, -4, W3D_TEXCOORD_NORMALIZED); } } #else #define Swap_TextureCoordPointers(c,e) {} #endif void GLEnableClientState(GLcontext context, GLenum state) { switch (state) { case GL_TEXTURE_COORD_ARRAY: context->ClientState |= GLCS_TEXTURE; Swap_TextureCoordPointers(context, GL_TRUE); break; case GL_COLOR_ARRAY: context->ClientState |= GLCS_COLOR; break; case GL_VERTEX_ARRAY: context->ClientState |= GLCS_VERTEX; break; default: GLFlagError(context, 1, GL_INVALID_ENUM); break; } } void GLDisableClientState(GLcontext context, GLenum state) { switch (state) { case GL_TEXTURE_COORD_ARRAY: context->ClientState &= ~GLCS_TEXTURE; Swap_TextureCoordPointers(context, GL_FALSE); break; case GL_COLOR_ARRAY: context->ClientState &= ~GLCS_COLOR; break; case GL_VERTEX_ARRAY: context->ClientState &= ~GLCS_VERTEX; break; default: GLFlagError(context, 1, GL_INVALID_ENUM); break; } } #if 0 //NYI void GLInterleavedArrays(GLcontext context, GLenum format, GLsizei stride, const GLvoid *pointer) { #define PTR context->ArrayPointer PTR.colorpointer = NULL; PTR.texcoordpointer = NULL; switch((int)format) { case GL_V2F: context->ClientState = GLCS_VERTEX; PTR.verts = (UBYTE *)pointer; if(stride == 0) PTR.vertexstride = 2*sizeof(GLfloat); else PTR.vertexstride = stride; break; case GL_V3F: context->ClientState = GLCS_VERTEX; PTR.verts = (UBYTE *)pointer; if(stride == 0) PTR.vertexstride = 3*sizeof(GLfloat); else PTR.vertexstride = stride; break; case GL_C4UB_V2F: context->ClientState = GLCS_VERTEX|GLCS_COLOR; PTR.colors = (UBYTE *)pointer; PTR.verts = ((UBYTE *)pointer + 4); PTR.colormode = W3D_COLOR_UBYTE | W3D_CMODE_RGBA; if(stride == 0) { PTR.vertexstride = PTR.colorstride = 2*sizeof(GLfloat) + 4*sizeof(GLubyte); } else { PTR.vertexstride = PTR.colorstride = stride; } break; case GL_C4UB_V3F: context->ClientState = GLCS_VERTEX|GLCS_COLOR; PTR.colors = (UBYTE *)pointer; PTR.verts = ((UBYTE *)pointer + 4); PTR.colormode = W3D_COLOR_UBYTE | W3D_CMODE_RGBA; if(stride == 0) { PTR.vertexstride = PTR.colorstride = 3*sizeof(GLfloat) + 4*sizeof(GLubyte); } else { PTR.vertexstride = PTR.colorstride = stride; } break; case GL_C3F_V3F: context->ClientState = GLCS_VERTEX|GLCS_COLOR; PTR.colors = (UBYTE *)pointer; PTR.verts = ((UBYTE *)pointer + 12); PTR.colormode = W3D_COLOR_FLOAT | W3D_CMODE_RGB; if(stride == 0) { PTR.vertexstride = PTR.colorstride = 6*sizeof(GLfloat); } else { PTR.vertexstride = PTR.colorstride = stride; } break; case GL_T2F_V3F: context->ClientState = GLCS_VERTEX|GLCS_TEXTURE; PTR.texcoords = (UBYTE *)pointer; PTR.verts = ((UBYTE *)pointer + 8); if(stride == 0) { PTR.vertexstride = PTR.texcoordstride = 5*sizeof(GLfloat); } else { PTR.vertexstride = PTR.texcoordstride = stride; } break; case GL_T2F_C4UB_V3F: context->ClientState = GLCS_VERTEX|GLCS_TEXTURE|GLCS_COLOR; PTR.texcoords = (UBYTE *)pointer; PTR.colors = ((UBYTE *)pointer + 8); PTR.verts = ((UBYTE *)pointer + 12); PTR.colormode = W3D_COLOR_UBYTE | W3D_CMODE_RGBA; if(stride == 0) { PTR.vertexstride = PTR.colorstride = PTR.texcoordstride = 5*sizeof(GLfloat) + 4*sizeof(GLubyte); } else { PTR.vertexstride = PTR.colorstride = PTR.texcoordstride = stride; } break; case GL_T2F_C3F_V3F: context->ClientState = GLCS_VERTEX|GLCS_TEXTURE|GLCS_COLOR; PTR.texcoords = (UBYTE *)pointer; PTR.colors = ((UBYTE *)pointer + 8); PTR.verts = ((UBYTE *)pointer + 20); PTR.colormode = W3D_COLOR_FLOAT | W3D_CMODE_RGB; if(stride == 0) { PTR.vertexstride = PTR.colorstride = PTR.texcoordstride = 8*sizeof(GLfloat); } else { PTR.vertexstride = PTR.colorstride = PTR.texcoordstride = stride; } break; } #undef PTR } #endif static GLboolean cur_pipeline_state = GL_TRUE; void GLTexCoordPointer(GLcontext context, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) { int w3dStride; int w_off; BYTE *base, *target; //check pipeline state to make sure that w_off was calculated: if((context->VertexArrayPipeline == cur_pipeline_state) && (context->ArrayPointer.texcoords == (UBYTE*)pointer)) return; #ifdef VA_SANITY_CHECK if(!pointer) //thanks to OF for making me aware.. { context->ArrayPointer.texcoords = (UBYTE*)&context->VertexBuffer[0].v.u; context->ArrayPointer.texcoordstride = sizeof(MGLVertex); context->ArrayPointer.w_off = -4; Set_W3D_TexCoordPointer(context->w3dContext, (void*)&context->VertexBuffer[0].v.u, sizeof(MGLVertex), 0, sizeof(GLfloat), -4, W3D_TEXCOORD_NORMALIZED); return; } #endif if (stride == 0) w3dStride = size*sizeof(GLfloat); else w3dStride = stride; if (type != GL_FLOAT) GLFlagError(context, 1, GL_INVALID_ENUM); context->ArrayPointer.texcoords = (UBYTE*)pointer; context->ArrayPointer.texcoordstride = w3dStride; cur_pipeline_state = context->VertexArrayPipeline; if(context->VertexArrayPipeline == GL_FALSE) { if (size != 4) GLFlagError(context, 1, GL_INVALID_VALUE); Set_W3D_TexCoordPointer(context->w3dContext, (void*)context->ArrayPointer.texcoords, 4*sizeof(GLfloat), 0, sizeof(GLfloat), 3*sizeof(GLfloat), W3D_TEXCOORD_NORMALIZED); } else { //find w_array-offset relative to current pointer: target = (BYTE*)&context->WBuffer[0]; base = (BYTE*)pointer; if(target < base) { w_off = (base - target); w_off = -w_off; } else { w_off = (target - base); } context->ArrayPointer.w_off = w_off; Set_W3D_TexCoordPointer(context->w3dContext, (void*)context->ArrayPointer.texcoords, w3dStride, 0, sizeof(GLfloat), w_off, W3D_TEXCOORD_NORMALIZED); } } void GLColorPointer(GLcontext context, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) { int w3dStride; ULONG w3dType, w3dFormat; if(context->ArrayPointer.colors == (UBYTE*)pointer) return; #ifdef VA_SANITY_CHECK if(!pointer) //thanks to OF for making me aware.. { context->ArrayPointer.colors = (UBYTE*)&context->VertexBuffer[0].color[0]; context->ArrayPointer.colorstride = sizeof(MGLVertex); context->ArrayPointer.colormode = W3D_COLOR_UBYTE | W3D_CMODE_RGBA; Convert = (Convfn)Convert_UB_RGBA; Set_W3D_ColorPointer(context->w3dContext, (void *)&context->VertexBuffer[0].color[0], sizeof(MGLVertex), W3D_COLOR_UBYTE, W3D_CMODE_RGBA, 0); return; } #endif if (type != GL_UNSIGNED_BYTE && type != GL_FLOAT && type != MGL_UBYTE_BGRA && type != MGL_UBYTE_ARGB) GLFlagError(context, 1, GL_INVALID_ENUM); if (stride == 0) w3dStride = size * (type == GL_FLOAT ? sizeof(GLfloat) : sizeof(GLubyte)); else w3dStride = stride; context->ArrayPointer.colors = (UBYTE*)pointer; context->ArrayPointer.colorstride = w3dStride; if(type == GL_FLOAT) w3dType = W3D_COLOR_FLOAT; else w3dType = W3D_COLOR_UBYTE; if (type != MGL_UBYTE_ARGB) { if(type == GL_FLOAT) { if(size == 3) { w3dFormat = W3D_CMODE_RGB; Convert = (Convfn)Convert_F_RGB; } else { w3dFormat = W3D_CMODE_RGBA; Convert = (Convfn)Convert_F_RGBA; } } else if (type == MGL_UBYTE_BGRA) { if(size == 3) { w3dFormat = W3D_CMODE_BGR; Convert = (Convfn)Convert_UB_BGR; } else { w3dFormat = W3D_CMODE_BGRA; Convert = (Convfn)Convert_UB_BGRA; } } else // GL_UNSIGNED_BYTE { if(size == 3) { w3dFormat = W3D_CMODE_RGB; Convert = (Convfn)Convert_UB_RGB; } else { w3dFormat = W3D_CMODE_RGBA; Convert = (Convfn)Convert_UB_RGBA; } } } else { w3dFormat = W3D_CMODE_ARGB; Convert = (Convfn)Convert_UB_ARGB; } context->ArrayPointer.colormode = w3dType | w3dFormat; Set_W3D_ColorPointer(context->w3dContext, (void *)context->ArrayPointer.colors, w3dStride, w3dType, w3dFormat, 0); } void GLVertexPointer(GLcontext context, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer) { int w3dStride; if (size < 3 || type != GL_FLOAT || type != GL_INT) GLFlagError(context, 1, GL_INVALID_VALUE); if(context->ArrayPointer.verts == (UBYTE*)pointer) return; #ifdef VA_SANITY_CHECK if(!pointer) //thanks to OF for making me aware.. { context->ArrayPointer.verts = (UBYTE*)&context->VertexBuffer[0].bx; context->ArrayPointer.vertexstride = sizeof(MGLVertex); if (Reset_W3D_VertexPointer == GL_TRUE) { Set_W3D_VertexPointer(context->w3dContext, (void *) &(context->VertexBuffer->bx), sizeof(MGLVertex), W3D_VERTEX_F_F_F, 0); Reset_W3D_VertexPointer = GL_FALSE; } return; } #endif if(type == GL_INT) { context->ArrayPointer.FixpointTrans = GL_TRUE; if (stride == 0) w3dStride = size * sizeof(GLint); else w3dStride = stride; } else { context->ArrayPointer.FixpointTrans = GL_FALSE; if (stride == 0) w3dStride = size * sizeof(GLfloat); else w3dStride = stride; } context->ArrayPointer.verts = (UBYTE*)pointer; context->ArrayPointer.vertexstride = w3dStride; if(context->VertexArrayPipeline == GL_FALSE) { if(type == GL_INT) GLFlagError(context, 1, GL_INVALID_VALUE); Reset_W3D_VertexPointer = GL_TRUE; //not default Set_W3D_VertexPointer(context->w3dContext, (void *)pointer, w3dStride, W3D_VERTEX_F_F_F, 0); } else if (Reset_W3D_VertexPointer == GL_TRUE) { Set_W3D_VertexPointer(context->w3dContext, (void *) &(context->VertexBuffer->bx), sizeof(MGLVertex), W3D_VERTEX_F_F_F, 0); Reset_W3D_VertexPointer = GL_FALSE; } } static INLINE void PreDraw(GLcontext context) { if (context->FogDirty && context->Fog_State) { fog_Set(context); context->FogDirty = GL_FALSE; } if (context->ShadeModel == GL_FLAT && context->UpdateCurrentColor == GL_TRUE) { context->UpdateCurrentColor = GL_FALSE; W3D_SetCurrentColor(context->w3dContext, &context->CurrentColor); } #ifdef AUTOMATIC_LOCKING_ENABLE if (context->LockMode == MGL_LOCK_MANUAL) return; else if (context->LockMode == MGL_LOCK_AUTOMATIC) // Automatic: Lock per primitive { if (W3D_SUCCESS == W3D_LockHardware(context->w3dContext)) { context->w3dLocked = GL_TRUE; } else { printf("Error during LockHardware\n"); } } else // Smart: Lock timer based { if (context->w3dLocked == GL_FALSE) { if (W3D_SUCCESS != W3D_LockHardware(context->w3dContext)) { return; // give up } context->w3dLocked = GL_TRUE; TMA_Start(&(context->LockTime)); } } #endif } static INLINE void PostDraw(GLcontext context) { context->VertexBufferPointer = 0; #ifdef AUTOMATIC_LOCKING_ENABLE if (context->LockMode == MGL_LOCK_SMART && TMA_Check(&(context->LockTime)) == GL_TRUE) { // Time to unlock W3D_UnLockHardware(context->w3dContext); context->w3dLocked = GL_FALSE; } else if (context->LockMode == MGL_LOCK_AUTOMATIC) { W3D_UnLockHardware(context->w3dContext); context->w3dLocked = GL_FALSE; } #endif } void GLDrawArrays(GLcontext context, GLenum mode, const GLint first, const GLsizei count) { ULONG error, prim; int k; #ifdef VA_SANITY_CHECK GLboolean ShadeModel_bypass; GLboolean TexCoord_bypass; #endif //11-04-02 - check for compiled arrays //sorry - no time to write proper handling for this. //Anyways, glDrawArrays is rarely used within a lock. if(context->ArrayPointer.transformed == GL_TRUE && Clip_Volume_Bypass == GL_FALSE) { GLDrawElements(context, mode, count, GL_UNSIGNED_SHORT, (void*)&(e_wrap[first])); return; } #ifdef VA_SANITY_CHECK ShadeModel_bypass = GL_FALSE; //Thanks to OF for making me aware of this unlikely case if(!(context->ClientState & GLCS_COLOR) && context->ShadeModel == GL_SMOOTH) { W3D_SetState(context->w3dContext, W3D_GOURAUD, W3D_DISABLE); context->ShadeModel = GL_FLAT; ShadeModel_bypass = GL_TRUE; } #endif PreDraw(context); #ifdef VA_SANITY_CHECK TexCoord_bypass = GL_FALSE; if(context->Texture2D_State[0] == GL_FALSE || !(context->ClientState & GLCS_TEXTURE)) { Set_W3D_Texture(context->w3dContext, 0, NULL); if(context->ClientState & GLCS_TEXTURE) { context->ClientState &= ~GLCS_TEXTURE; TexCoord_bypass = GL_TRUE; } } #else if(context->Texture2D_State[0] == GL_FALSE) { Set_W3D_Texture(context->w3dContext, 0, NULL); } #endif else { Set_W3D_Texture(context->w3dContext, 0, context->w3dTexBuffer[context->CurrentBinding]); } if(context->VertexArrayPipeline == GL_FALSE) { switch(mode) { case GL_POINTS: prim = W3D_PRIMITIVE_POINTS; break; case GL_LINE_STRIP: prim = W3D_PRIMITIVE_LINESTRIP; break; case GL_LINE_LOOP: prim = W3D_PRIMITIVE_LINELOOP; break; case GL_LINES: prim = W3D_PRIMITIVE_LINES; break; case GL_TRIANGLE_STRIP: prim = W3D_PRIMITIVE_TRISTRIP; break; case MGL_FLATFAN: case GL_POLYGON: case GL_TRIANGLE_FAN: prim = W3D_PRIMITIVE_TRIFAN; break; case GL_TRIANGLES: prim = W3D_PRIMITIVE_TRIANGLES; break; default: GLFlagError(context, 1, GL_INVALID_ENUM); } error = W3D_DrawArray(context->w3dContext, prim, first, count); } else { if(context->ArrayPointer.transformed == GL_TRUE) { context->VertexBufferPointer = context->ArrayPointer.lockfirst + context->ArrayPointer.locksize; } else { context->VertexBufferPointer = first + count; } #if 0 if((count == 3) && (mode != MGL_FLATFAN)) { A_DrawTriangle(context, first); } else #endif switch(mode) { case GL_TRIANGLE_STRIP: A_DrawTriStrip(context, first, count); break; case GL_TRIANGLE_FAN: A_DrawTriFan(context, first, count); break; case GL_TRIANGLES: A_DrawTriangles(context, first, count); break; case GL_POLYGON: A_DrawPolygon(context, first, count); break; case MGL_FLATFAN: A_DrawFlatFan(context, first, count); break; default: GLFlagError(context, 1, GL_INVALID_ENUM); } } PostDraw(context); #ifdef VA_SANITY_CHECK if(ShadeModel_bypass == GL_TRUE) { W3D_SetState(context->w3dContext, W3D_GOURAUD, W3D_ENABLE); context->ShadeModel = GL_SMOOTH; } if(TexCoord_bypass == GL_TRUE) { context->ClientState |= GLCS_TEXTURE; } #endif } #ifdef __VBCC__ #undef V_ToScreen #undef A_ToV #endif